package dp.sgd;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;

import org.apache.mahout.classifier.ClassifierResult;
import org.apache.mahout.classifier.ResultAnalyzer;
import org.apache.mahout.classifier.sgd.ModelSerializer;
import org.apache.mahout.classifier.sgd.OnlineLogisticRegression;
import org.apache.mahout.math.Vector;
import org.apache.mahout.vectorizer.encoders.Dictionary;

import com.google.common.collect.HashMultiset;
import com.google.common.collect.Lists;
import com.google.common.collect.Multiset;

import dp.utils.Utils;

/**
 *	inspired by TestNewsGroups
 * 	usage SGDTester [single|multi] [path-to-model-file] [path-to-testFiles(if multi)|string-input(if single)] [path-to-categories-file(if single)]
 *
 */
public class SGDTester {
	
	public static void main(String[] args) {
		
		SGDTester tester = new SGDTester();
		
		if (args.length < 3) {
			System.err.println("zly pocet parametrov: usage SGDTester [multi|single] [path-to-model-file] [path-to-testFiles] [path-to-categories-file(if single)]");
			System.exit(1);
		}
		String pathToModel = args[1];
		if ("single".equals(args[0])) {
			String testFilePath = args[2];
			String categoriesFile = args[3];
			String text = Utils.readFileAsString(testFilePath); 
			tester.testSingle(text, pathToModel, categoriesFile);
		}
		else if ("multi".equals(args[0])) {
			String testFilesDirPath = args[2];
			tester.testMulti(testFilesDirPath,pathToModel);
		}
		else {
			System.err.println("zly prepinac: pripustne hodnoty pre typ testu: [single|multi]");
			System.exit(1);
		}
		
	}
	
	public ClassifierResult testSingle(String text, String modelFilePath, String categoriesFile) {
		List<String> categories = getCategories(categoriesFile);
		Dictionary dictionary = new Dictionary();
		OnlineLogisticRegression classifier = null;
		try {
			classifier = ModelSerializer.readBinary(new FileInputStream(modelFilePath), OnlineLogisticRegression.class);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		for (String categoryName : categories) {
			dictionary.intern(categoryName);
		}
		Multiset<String> overallCounts = HashMultiset.create();
		Helper helper = new Helper();
		Vector input = helper.encodeFeatureVectorRaw(text, overallCounts);

		Vector result = classifier.classifyFull(input);
		int cat = result.maxValueIndex();
		
		double score = result.maxValue();
		double ll = classifier.logLikelihood(0, input);
		ClassifierResult cr = new ClassifierResult(dictionary.values().get(cat), score, ll);
		Utils.log("cat = " + cr.getLabel() + "\nscore = " + score + "\nll = " + ll);
		return cr;
	}
	
	public void testMulti(String testDirPath, String modelFilePath) {
		File base = new File (testDirPath);
		OnlineLogisticRegression classifier = null;
		try {
			classifier = ModelSerializer.readBinary(
					new FileInputStream(modelFilePath), OnlineLogisticRegression.class);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		Dictionary dictionary = new Dictionary();
		Multiset<String> overallCounts = HashMultiset.create();

		List<Record> records = Lists.newArrayList();
		
		for (File newsgroup : base.listFiles()) {
			dictionary.intern(newsgroup.getName());
			records = SGDAnalyzer.addRecordsToCollection(newsgroup, records);
		}
		
		Utils.log(records.size() + " test records");
		
		ResultAnalyzer ra = new ResultAnalyzer(dictionary.values(), "DEFAULT");
		
		for (Record record : records) {
			String category = record.getCategory();
			int actual = dictionary.intern(category);
			Helper helper = new Helper();
			Vector input = helper.encodeFeatureVector(record, actual, overallCounts);
			Vector result = classifier.classifyFull(input);
			int cat = result.maxValueIndex();
			double score = result.maxValue();
			double ll = classifier.logLikelihood(actual, input);
			ClassifierResult cr = new ClassifierResult(dictionary.values().get(cat), score, ll);
//			Utils.log("\ncat = " + cr.getLabel() + "\nscore = " + score + "\nll = " + ll);
			ra.addInstance(dictionary.values().get(actual), cr);
		}
		Utils.log(ra.toString());
	}

	private List<String> getCategories(String filePath) {
		List<String> categories = new ArrayList<String>();
		try {
			FileInputStream fstream = new FileInputStream(filePath);
			DataInputStream in = new DataInputStream(fstream);
			BufferedReader br = new BufferedReader(new InputStreamReader(in));
			String strLine;
			while ((strLine = br.readLine()) != null) {
				if (!strLine.isEmpty()) {
					categories.add(strLine.trim());
				}
			}
			in.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return categories;
	}
	
}
